package com.pzj.export;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PushbackInputStream;
import java.io.UnsupportedEncodingException;
import java.util.Iterator;
import java.util.List;
import java.util.zip.Deflater;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.poi.POIXMLDocument;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import com.pzj.export.config.Align;
import com.pzj.export.config.BaseConfig;
import com.pzj.export.config.CellColConfig;
import com.pzj.export.config.ColHeaderColConfig;
import com.pzj.export.config.ColHeaderConfig;
import com.pzj.export.config.ColHeaderRowConfig;
import com.pzj.export.config.ExcelVersion;
import com.pzj.export.config.ExportConfiguration;
import com.pzj.export.config.FooterConfig;
import com.pzj.export.config.HeaderConfig;
import com.pzj.export.config.TableConfig;
import com.pzj.export.config.TitleConfig;
import com.pzj.export.exception.ExportException;
import com.pzj.export.util.PropertyValueUtil;

/**
 * <p><b>按配置导出Excel文件</b></p> 
 * <p>从1.1开始，建议从ExportFacotry.getCurrentExport获取实例</p>
 * @author liulm
 * @author Puzhijun
 * @version 1.1
 */
public class ExportExcel<T> implements Export<T> {
	private static final Log log = LogFactory.getLog(ExportExcel.class);

	/** excel2003后缀名 */
	protected static final String EXCEL_2003_SUFFIX = ".xls";
	/** excel2007后缀名 */
	protected static final String EXCEL_2007_SUFFIX = ".xlsx";
	/** poi中每个字符的宽度 */
	protected static final short POI_CHARACTER_WIDTH = 256;
	/** 灰色背景默认颜色 */
	protected static final int GRAY_BACKGROUND_COLOR = 0xC1C1C1;
	/** 字体默认颜色 */
	protected static final int FONT_DETAULT_COLOR = 0xffffff;
	
	/** 序号列的列头 */
	protected static final String SERIAL_HEADER_NAME = "序号";
	
	/** 导出excel配置 */
	protected ExportConfiguration exportConfiguration;
	/** 临时数据文件名称 */
	protected String tempExcelDicName;
	/** 当前临时文件的计数器 */
	protected int currentFileCounter;
	/** 导出的结果条数 */
	protected int result;
	/** sheet名称  */
	@Deprecated
	protected String sheetName = "导出数据";
	
	/** 一行数据的提供者 */
	private ExportRowDataHolder exportRowDataHolder = new ExportRowDataBaseHolder();
	
	/**
	 * 一行数据的提供者
	* @Title: getExportRowDataHolder
	* @Description: 一行数据的提供者
	* @return ExportRowDataHolder
	* @throws
	 */
	public ExportRowDataHolder getExportRowDataHolder() {
		return exportRowDataHolder;
	}

	/**
	 * 一行数据的提供者
	* @Title: setExportRowDataHolder
	* @Description: 一行数据的提供者
	* @param exportRowDataHolder
	* @return void
	* @throws
	 */
	public void setExportRowDataHolder(ExportRowDataHolder exportRowDataHolder) {
		this.exportRowDataHolder = exportRowDataHolder;
	}

	/**
	 * 获取导出excel配置
	 * */
	public ExportConfiguration getExportConfiguration() {
		return exportConfiguration;
	}

	/**
	 * 设置excel导出配置
	 * */
	public void setExportConfiguration(ExportConfiguration exportConfiguration) {
		this.exportConfiguration = exportConfiguration;
	}


	/**
	 * 获得sheet名称
	 * 此方法已经过时,请使用setExportConfiguration方法
	 * @return 
	 * @see ExportConfiguration
	 */
	@Deprecated
	public String getSheetName() {
		return sheetName;
	}

	/**
	 * 设置sheet名称
	 * 此方法已经过时，请使用getExportConfiguration方法
	 * @param sheetName
	 *  @see ExportConfiguration
	 */
	@Deprecated
	public void setSheetName(String sheetName) {
		if (sheetName != null && sheetName.length() > 0)
			this.sheetName = sheetName;
	}

	
	/**
	 * 生成临时的excel文件
	 * @param list 生成临时excel文件的数据
	 * @param tableConfig excel配置
	 * @return 结果数
	 * @throws ExportException
	 */
	protected int makeTempExcel(List<T> list, TableConfig<T> tableConfig) throws ExportException {
		List<CellColConfig<T>> cellColConfigList = tableConfig.getCellCols();
		if (cellColConfigList == null || cellColConfigList.size() == 0)
			throw new IllegalArgumentException("The column config is empty");
		
		// 获得显示列的配置。
		CellColConfig<T>[] cellColArr = CellColConfig.getShowCols(cellColConfigList);
		// 添加序号头
		if (tableConfig.isShowSerialNumber()) cellColArr = exportRowDataHolder.addSerialNumber(cellColArr);
		int totalColNum = cellColArr.length;
		
		// 临时文件夹的名字生成策略
		String tempFolderName = "exportTemp_" + System.nanoTime();
		// 临时文件的存放目录
		String tempDic = getTempDic();
		if(!tempDic.matches("/$")) {
			tempDic += "/";
		}
		if(tempExcelDicName == null) tempExcelDicName = tempDic + tempFolderName;
		File dic = new File(tempExcelDicName);
		if(!dic.exists()) dic.mkdirs();
		FileOutputStream fos = null;
		
		try {
			fos = new FileOutputStream(new File(dic, 
					getTempExcelFileName() + "_" + String.valueOf(++currentFileCounter) + 
					getExcelSuffixNameByConfigExcelVersion()));
			result = exportOneWorkbook(list, tableConfig, null, fos, totalColNum, cellColArr, 
					result * (currentFileCounter - 1));
		} catch (FileNotFoundException e) {
			result = -1;
			log.error(e.getMessage(), e);
			throw new ExportException("文件导出异常", e);
		} catch (IOException e) {
			result = -1;
			log.error(e.getMessage(), e);
			throw new ExportException("文件导出异常", e);
		} finally {
			if(fos != null) {
				try {
					fos.close();
				} catch (IOException e) {
					result = -1;
					log.error(e.getMessage(), e);
					throw new ExportException("文件导出异常", e);
				}
			}
		}
		return result;
	}
	
	/**
	 * 压缩成zip文件并且用输出流进行输出
	 * @param out 输出流
	 * @throws ExportException 关闭导出的时候出现异常
	 */
	private void zip(OutputStream out) throws ExportException {
		if(out == null) throw new ExportException("OutputStream must not null");
		org.apache.tools.zip.ZipOutputStream  zipOs = new org.apache.tools.zip.ZipOutputStream(out);
		zipOs.setEncoding("gbk");
		zipOs.setLevel(Deflater.BEST_SPEED);
		try {
			File tempDic = new File(tempExcelDicName);
			File[] files = tempDic.listFiles();
			for(File f : files) {
				if(f.getName().indexOf(getExcelSuffixNameByConfigExcelVersion()) == -1) continue;
				zipOs.putNextEntry(new org.apache.tools.zip.ZipEntry(f.getName()));
				FileInputStream fis = new FileInputStream(f);
				BufferedInputStream bfis = new BufferedInputStream(fis);
				int c;
				byte[] buffer = new byte[2048];
				while((c = bfis.read(buffer)) != -1) {
					zipOs.write(buffer, 0, c);
				}
				bfis.close();
				fis.close();
			}
			zipOs.flush();
			ExportFactory.getInstance().destroyExportExcel();
		} catch (FileNotFoundException e) {
			log.error(e.getMessage(), e);
			throw new ExportException("关闭export过程中出现异常", e);
		} catch (IOException e) {
			log.error(e.getMessage(), e);
			throw new ExportException("关闭export过程中出现异常", e);
		} finally {
			try {
				zipOs.close();
			} catch (IOException e) {
				log.error(e.getMessage(), e);
				throw new ExportException("关闭export过程中出现异常", e);
			}
			
		}
	}
	
	/**
	 * 导出一个临时的excel
	 * @param list 要导出的数据
	 * @param tableConfig excel配置
	 * @param in 输入流
	 * @param out 输出流
	 * @param totalConNum 总列数
	 * @param cellColArr 列配置
	 * @param serialNumber 起始序号
	 * @return
	 * @throws IOException 导出excel时抛出异常
	 */
	protected int exportOneWorkbook(List<T> list, TableConfig<T> tableConfig,
			InputStream in, OutputStream out, int totalColNum, 
			CellColConfig<T>[] cellColArr, int serialNumber) throws IOException {
		// 为了兼容以前的版本
		String localSheetName = sheetName;
		if(exportConfiguration != null) localSheetName = exportConfiguration.getExcelSheetName();
		Workbook workbook = null;
		try {
			workbook = getWorkbookByConfigExcelVersion(in);
		} catch (InvalidFormatException e) {
			log.error(e.getMessage(), e);
		} 
		
		// 如果是有设置了每个工作簿的导出数量
		if(exportConfiguration != null && exportConfiguration.getDataSizePerSheet() > 0) {
			result = splitDataToSheets(list, exportConfiguration.getDataSizePerSheet(), workbook,
					localSheetName, tableConfig, totalColNum, cellColArr, serialNumber);
		} else {
			// 如果没有设置每个工作簿的大小，也要进行数据是否超过最大行
			final int maxRows = ExportConfiguration.MAX_DATA_SIZE_PER_SHEET;
			if(list.size() < maxRows) {
				result += exportOneSheet(workbook, 0, localSheetName, tableConfig, 
						totalColNum, list, cellColArr, serialNumber);
			} else {
				result = splitDataToSheets(list, maxRows, workbook, localSheetName, 
						tableConfig, totalColNum, cellColArr, serialNumber);
			}
		}
		workbook.write(out);
		return result;
	}
	
	/**
	 * 把数据分割成几个sheet
	 * @param list 数据
	 * @param size 每个sheet大小
	 * @param workbook excel文件
	 * @param sheetName 工作簿名称
	 * @param tableConfig excel文件配置
	 * @param totalColNum 总共的列数
	 * @param cellColArr 列配置
	 * @param serialNumber 序号
	 * @return
	 */
	protected int splitDataToSheets(List<T> list, int size, Workbook workbook, String sheetName,
			TableConfig<T> tableConfig, int totalColNum, CellColConfig<T>[] cellColArr,
			int serialNumber) {
		final int counter = ((Double)Math.ceil((double)list.size() / (double)size)).intValue();
		for(int i = 0; i < counter; i++) {
			List<T> data = list.subList(i * size, 
					(i + 1) * size >= list.size() ? list.size() : (i + 1) * size);
			result += exportOneSheet(workbook, i, sheetName + (i + 1), tableConfig, 
					totalColNum, data, cellColArr, serialNumber + result);
		}
		return result;
	}
	
	/**
	 * 构造一个工作簿(sheet)
	 * @param workbook excel文件
	 * @param index 工作簿位置
	 * @param sheetName 工作簿名称
	 * @param tableConfig excel配置
	 * @param totalColNum 总共的列数
	 * @param list 需要的数据
	 * @param cellColArr 列配置数组
	 * @param serialNumber 序号
	 */
	protected int exportOneSheet(Workbook workbook, int index, String sheetName, 
			TableConfig<T> tableConfig, int totalColNum, List<T> list, 
			CellColConfig<T>[] cellColArr, int serialNumber) {
		Sheet sheet = workbook.createSheet(sheetName);
		workbook.setSheetOrder(sheetName, index);
		// 记录当前操作的行数
		int currentOutputRow = 0;

		if (tableConfig.getTableFirstRowNum() > 1)
			currentOutputRow = tableConfig.getTableFirstRowNum() - 1;
		// title
		if (tableConfig.getTitle() != null
				&& tableConfig.getTitle().getShowLocation() == TitleConfig.TOP) {
			currentOutputRow += setTitle(tableConfig.getTitle(),
					totalColNum, currentOutputRow, sheet, workbook);
		}

		// header
		currentOutputRow += setHeader(tableConfig.getHeader(), totalColNum,
				currentOutputRow, sheet, workbook);

		// 根据设置的列头输出
		currentOutputRow += setColHeader(tableConfig, currentOutputRow,
				tableConfig.isShowSerialNumber(), cellColArr, sheet, workbook);
		int result = -1;
		// 表格数据
		result = setTableData(list, currentOutputRow, cellColArr,
				tableConfig.isShowSerialNumber(), sheet, workbook, serialNumber);
		currentOutputRow += result;

		// 设定列宽
		int[] colCharNum = getColCharNum(list, cellColArr, tableConfig);
		setColWidth(colCharNum, sheet);

		// footer
		currentOutputRow += setFooter(tableConfig.getFooter(), totalColNum,
				currentOutputRow, sheet, workbook);

		// title
		if (tableConfig.getTitle() != null
				&& tableConfig.getTitle().getShowLocation() == TitleConfig.BOTTOM) {
			currentOutputRow += setTitle(tableConfig.getTitle(),
					totalColNum, currentOutputRow, sheet, workbook);
		}
		return result;
	}
	
	/**
	 * 验证参数，如果没有符合则抛出异常
	 * @param list 要显示的数据
	 * @param tableConfig excel配置
	 * @param out 输出流
	 * @throws IllegalArgumentException 
	 * 	list 为null时
	 * 	tableConfig 为null时
	 * 	out 为null时
	 */
	protected void validateParameters(List<T> list, TableConfig<T> tableConfig, OutputStream out) {
		if (list == null)
			throw new IllegalArgumentException("The exported data must not null");
		if (tableConfig == null)
			throw new IllegalArgumentException("The exported config must not null");
		if (out == null) {
			throw new IllegalArgumentException("The output stream must not null");
		}
		
	}
	
	/**
	 * 设置列的宽度。
	 * @param colWidth
	 * @param sheet
	 */
	private void setColWidth(int[] colWidth, Sheet sheet) {
		for (int i = 0; i < colWidth.length; i++) {
			sheet.setColumnWidth(i, colWidth[i]);
		}
	}

	/**
	 * 构造列头
	 * @param headerConfig 表头配置
	 * @param totalColNum 总共的列数
	 * @param currentOutputRow 当前的行
	 * @param sheet 工作簿
	 * @return 返回头部条占的行数。
	 */
	private int setHeader(HeaderConfig headerConfig, int totalColNum,
			int currentOutputRow, Sheet sheet, Workbook workbook) {
		int currentRow = currentOutputRow;
		if (headerConfig != null) {
			String headerContent = headerConfig.getContent();

			CellStyle cellStyle = createHeaderStyle(headerConfig, workbook);
			
			// 输出
			Row row = sheet.createRow(currentRow);
			Cell cell = row.createCell(0);
			cell.setCellStyle(cellStyle);
			cell.setCellValue(headerContent);
			sheet.addMergedRegion(new CellRangeAddress(currentRow, currentRow, 0, totalColNum - 1));
			row.setHeight((short) getExcelHeight(headerConfig.getHeight()));
			currentRow++;
		}
		return currentRow - currentOutputRow;
	}

	/**
	 * 构造footer信息
	 * @param footerConfig footer配置
	 * @param totalColNum 总共的列数
	 * @param currentOutputRow 当前的行数
	 * @param sheet excel工作簿
	 * @param workbook excel实例
	 * @return 返回底部条占的行数。
	 */
	private int setFooter(FooterConfig footerConfig, int totalColNum,
			int currentOutputRow, Sheet sheet, Workbook workbook) {
		int currentRow = currentOutputRow;
		if (footerConfig != null) {
			String content = footerConfig.getContent();

			CellStyle cellStyle = createFooterStyle(footerConfig, workbook);
			
			// 输出
			Row row = sheet.createRow(currentRow);
			Cell cell = row.createCell(0);
			cell.setCellStyle(cellStyle);
			cell.setCellValue(content);
			sheet.addMergedRegion(new CellRangeAddress(currentRow, currentRow, 0, totalColNum - 1));
			
			currentRow++;
		}
		return currentRow - currentOutputRow;
	}

	/**
	 * 设置除开title和footer意外的数据行
	 * @param list 要进行数据设置的行数据List
	 * @param currentOutputRow 当前输入的行数
	 * @param cellColArr 单元格配置数组
	 * @param showSerialNumber 是否显示序号
	 * @param sheet excel sheet
	 * @return 返回输出的行数。
	 */
	protected int setTableData(List<T> list, int currentOutputRow,
			CellColConfig<T>[] cellColArr, boolean showSerialNumber,
			Sheet sheet, Workbook workbook, int serialNumber) {
		// 已输出的行数
		int currentRow = currentOutputRow;
		Iterator<T> objIter = list.iterator();
		while (objIter.hasNext()) {
			T recordObj = objIter.next();

			String[] recordArr = null;
			// 设置序号
			if (showSerialNumber) {
				recordArr = exportRowDataHolder.getOneRowDataAddSerialNumber(recordObj, cellColArr,
						String.valueOf(++serialNumber));
			} else {
				recordArr = exportRowDataHolder.getOneRowData(recordObj, cellColArr);
			}
			
			Row row = sheet.createRow(currentRow);
			
			for (int i = 0; i < cellColArr.length; i++) {
				CellStyle cellStyle = createNormalCellStyle(cellColArr[i], workbook);
				Cell cell = row.createCell(i);
				cell.setCellStyle(cellStyle);
				cell.setCellValue(recordArr[i]);
			}
			currentRow++;
		}

		return currentRow - currentOutputRow;
	}

	/**
	 * 求得每列平均字符数。每列字符不超过30个
	 * @param list 行数据对象
	 * @param cellColArr 单元格配置数组
	 * @param tableConfig 表格配置对象
	 * @return
	 */
	private int[] getColAverageCharNum(List<T> list,
			CellColConfig<T>[] cellColArr, TableConfig<T> tableConfig) {
		return getColAverageCharNum(list, cellColArr, tableConfig, -1);
	}

	/**
	 * 求得每列平均字符数。每列字符不超过30个
	 * @param list 行数据list对象 
	 * @param cellColArr 单元格配置数组
	 * @param tableConfig 表格配置对象
	 * @param maxSerialNumber 最大序号 当小于等于-1时，不显示序号。
	 * @return
	 */
	protected int[] getColAverageCharNum(List<T> list,
			CellColConfig<T>[] cellColArr, TableConfig<T> tableConfig,
			int maxSerialNumber) {
		// 每列最大字符宽度
		int colCharMaxNum = 256;
		int[] colCharNums = new int[cellColArr.length];
		// 记录没一列的字符总数。
		long[] colTotalCharNums = new long[cellColArr.length];

		int recordSize = list.size();
		Iterator<T> objIter = list.iterator();
		while (objIter.hasNext()) {
			T recordObj = objIter.next();
			// 一行数据的每列字符
			String[] recordArr = null;
			if (maxSerialNumber >= 0) {
				recordArr = exportRowDataHolder.getOneRowDataAddSerialNumber(recordObj, cellColArr,
						String.valueOf(maxSerialNumber));
			} else {
				recordArr = exportRowDataHolder.getOneRowData(recordObj, cellColArr);
			}

			for (int i = 0; i < cellColArr.length; i++) {
				int charNum = PropertyValueUtil.getStringLengthForChar(recordArr[i]);
				if(cellColArr[i].getColHeader() != null && 
						!cellColArr[i].getColHeader().trim().equals("")) {
					String isoColHeader = "";
					try {
						isoColHeader = new String(cellColArr[i].getColHeader().getBytes(), 
								PropertyValueUtil.ISO_8859_1);
					} catch (UnsupportedEncodingException e) {
						log.warn("列头编码错误", e);
					}
					if(isoColHeader.length() > charNum) {
						charNum = isoColHeader.length();
					} 
				}
				// 每列最大不超过每列最大字符宽度
				if (charNum > colCharMaxNum)
					charNum = colCharMaxNum;
				if(charNum >= colTotalCharNums[i]) {
					colTotalCharNums[i] = charNum;
				}
			}
		}

		// 求得每列平均长度。
		for (int index = 0; index < cellColArr.length; index++) {
			// 因为按照当前字符数还是有折行现象。增加补偿值来消除折行。
			int redeemValue = 2 * POI_CHARACTER_WIDTH;

			if (recordSize > 0) {
				colCharNums[index] = ((int) (colTotalCharNums[index]) * POI_CHARACTER_WIDTH)
						+ redeemValue;
			}
			// 当没有数据时，宽度按设定值取
			else {
				colCharNums[index] = getExcelWidth(cellColArr[index].getWidth());
			}
		}

		return colCharNums;
	}

	/**
	 * 返回列字符数
	 * @param list 含数据对象
	 * @param cellColArr 单元格配置数组
	 * @param tableConfig 表格配置
	 * @return
	 */
	private int[] getColCharNum(List<T> list, CellColConfig<T>[] cellColArr,
			TableConfig<T> tableConfig) {
		int[] colCharNums = new int[cellColArr.length];

		if (tableConfig.isAutoColWidth()) {
			// 计算平均列宽
			if (tableConfig.isShowSerialNumber())
				colCharNums = getColAverageCharNum(list, cellColArr,
						tableConfig, String.valueOf(list.size()).length());
			else
				colCharNums = getColAverageCharNum(list, cellColArr,
						tableConfig);
		} else {
			for (int i = 0; i < cellColArr.length; i++) {
				colCharNums[i] = getExcelWidth(cellColArr[i].getWidth());
			}
		}

		// 锁定总宽度且总宽度设了大于0.1的值时，各列宽度按比例计算。
		if (tableConfig.isLockedWidth() && tableConfig.getTotalWidth() > 0.1) {
			int totalWidth = getExcelWidth(tableConfig.getTotalWidth());
			double colCharNumSum = 0;
			for (int i = 0; i < colCharNums.length; i++) {
				colCharNumSum += colCharNums[i];
			}

			for (int i = 0; i < colCharNums.length; i++) {
				colCharNums[i] = (int) (totalWidth * (colCharNums[i] / colCharNumSum));
			}
		}

		return colCharNums;
	}

	/**
	 * 创建单元格格式。
	 * @param cellConfig 单元格配置
	 * @param workbook 工作簿
	 * @return CellStyle 返回单元格样式
	 */
	private CellStyle createNormalCellStyle(BaseConfig cellConfig, Workbook workbook) {
		return createCellStyle(cellConfig, workbook, true);
	}
	
	/**
	 * 创建title的样式
	 * @param titleConfig title配置
	 * @param workbook excel表格
	 * */
	protected CellStyle createTitelStyle(TitleConfig titleConfig, Workbook workbook) {
		return createCellStyle(titleConfig, workbook, false);
	}

	/**
	 * 创建footer的样式
	 * @param footerConfig footer配置
	 * @param workbook excel工作簿实例
	 * */
	private CellStyle createFooterStyle(FooterConfig footerConfig, Workbook workbook) {
		return createCellStyle(footerConfig, workbook, false);
	}
	
	/**
	 * 构造头部的样式
	 * @param headerConfig 头部的配置
	 * @param workbook excel实例
	 * */
	private CellStyle createHeaderStyle(HeaderConfig headerConfig, Workbook workbook) {
		return createCellStyle(headerConfig, workbook, false);
	}
	
	/**
	 * 构造列头样式
	 * @param config 列头配置
	 * @param workbook excel实例
	 * */
	protected CellStyle createColHeaderStyle(ColHeaderColConfig config, Workbook workbook) {
		return createCellStyle(config, workbook, false);
	}
	
	/**
	 * 构造单元格样式
	 * @param styleConfig 样式配置
	 * @param workbook excel实例
	 * @param isGetHaven 是否从现有的workbook实例中获取
	 * @throws NullPointerException workbook为空
	 * @throws NullPointerException styleConfig为空
	 * */
	private CellStyle createCellStyle(BaseConfig styleConfig, Workbook workbook, boolean isGetHaven) {
		if(workbook == null) throw new NullPointerException("Workbook must not is null");
		if(styleConfig == null) throw new NullPointerException("styleConfig must not is null");
		
		Font detFont = null;
		// 为了避免构造过多的字体，从现有的workbook中获取第0个字体
		if(isGetHaven) {
			detFont = workbook.getFontAt((short)0);
		}
		if(detFont == null) detFont = workbook.createFont();
		detFont.setFontHeightInPoints((short)styleConfig.getFontPointSize());
		detFont.setColor((short)FONT_DETAULT_COLOR);
		detFont.setUnderline(Font.U_NONE);
		CellStyle cellStyle = null;
		// 避免构造过多的单元格格式， 从现有的workbook中获取第0个样式
		if(isGetHaven) {
			cellStyle = workbook.getCellStyleAt((short)0);
		}
		if(cellStyle == null) cellStyle = workbook.createCellStyle();

		if (styleConfig.isFontBold()) {
			detFont.setBoldweight(Font.BOLDWEIGHT_BOLD);
		}
		
		cellStyle.setFont(detFont);
		setAlignment(styleConfig.getAlign(), cellStyle);
		if (styleConfig.isBorder()) {
			cellStyle.setBorderBottom(CellStyle.BORDER_THIN);
			cellStyle.setBorderLeft(CellStyle.BORDER_THIN);
			cellStyle.setBorderRight(CellStyle.BORDER_THIN);
			cellStyle.setBorderTop(CellStyle.BORDER_THIN);
		}
		cellStyle.setWrapText(styleConfig.isWrap());
		if (styleConfig.isBackgroundGray()) {
			cellStyle.setFillBackgroundColor((short)GRAY_BACKGROUND_COLOR);
		}

		return cellStyle;
	}
	
	/**
	 * 设置列头
	 * @param tableConfig 表格输出配置
	 * @param currentOutputRow 当前输出开始行。重0开始 。
	 * @param cellColArr 单元格配置
	 * @param sheet 输出excel的sheet
	 * @param showSerialNumber 是否显示序列号
	 * @return 返回列头占的行数。
	 */
	private int setColHeader(TableConfig<T> tableConfig, int currentOutputRow,
			boolean showSerialNumber, CellColConfig<T>[] cellColArr,
			Sheet sheet, Workbook workbook) {
		int currentRow = currentOutputRow;
		if (tableConfig.getColHeader() != null) {
			currentRow = setComplexColHeader(tableConfig.getColHeader(),
					showSerialNumber, cellColArr, currentRow, sheet, workbook);
		// 如果未设置列头，根据列配置中的列头名显示。	
		} else {
			currentRow = setSimpleColHeader(cellColArr, currentRow,
					tableConfig.isColHeaderBackgroundGray(), sheet, workbook);
		}
		return currentRow - currentOutputRow;
	}

	/**
	 * 根据配置生成复合列头。
	 * @param colHeaderConfig 列头配置
	 * @param showSerialNumber 是否显示序号列
	 * @param cellColArr 列配置
	 * @param currentRow 当前的行
	 * @param sheet 工作簿
	 * @return
	 */
	private int setComplexColHeader(ColHeaderConfig colHeaderConfig,
			boolean showSerialNumber, CellColConfig<T>[] cellColArr,
			int currentRow, Sheet sheet, Workbook workbook) {
		// 以从左到右的顺序合并单元格。每次从列最小的行开始。
		// 配置复杂colheader.
		List<ColHeaderRowConfig> rows = colHeaderConfig.getRows();
		// 下一行的其实列序号
		int nextRowCellIndex = 1;
		// 有序号列
		if(!showSerialNumber) nextRowCellIndex = 0;
		if(rows == null || rows.size() == 0) return currentRow;
		
		for(ColHeaderRowConfig rowConfig : rows) {
			Row row = sheet.createRow(currentRow);
			List<ColHeaderColConfig> list = rowConfig.getCols();
			
			// 添加序号列
			if(showSerialNumber) {
				CellStyle cellStyle = createColHeaderStyle(rows.get(0).getColHeaderColConfig(0), workbook);
				Cell cell = row.createCell(0);
				cell.setCellStyle(cellStyle);
				cell.setCellValue(SERIAL_HEADER_NAME);
				sheet.addMergedRegion(new CellRangeAddress(currentRow, rows.size(), 0, 0));
				showSerialNumber = false;
			}	
			
			// 这行的其实列序号
			int cellIndex = nextRowCellIndex;
			for(ColHeaderColConfig cellConfig : list) {
				CellStyle cellStyle = createColHeaderStyle(cellConfig, workbook);
				Cell cell = row.createCell(cellIndex);
				cell.setCellValue(cellConfig.getContent());
				cell.setCellStyle(cellStyle);
				int colspan = cellConfig.getColspan();
				int rowspan = cellConfig.getRowspan();
				// 如果合并的行大于1
				if(rowspan > 1) nextRowCellIndex = nextRowCellIndex + colspan;
				if (colspan > 1 || rowspan > 1) {
					// 合并单元格
					sheet.addMergedRegion(new CellRangeAddress(currentRow, 
							currentRow + rowspan - 1, 
							cellIndex, cellIndex + colspan - 1));
					cellIndex = cellIndex + colspan - 1;
				}
				cellIndex++;
			}
			currentRow++;
		}
	
		return currentRow;
	}

	/**
	 * 生成简单表头
	 * @param cellColArr 单元格配置数组
	 * @param currentRow 当前行
	 * @param isBackgroundGray 是否显示灰色背景
	 * @param sheet excel sheet
	 * @return
	 */
	protected int setSimpleColHeader(CellColConfig<T>[] cellColArr, int currentRow,
			boolean isBackgroundGray, Sheet sheet, Workbook workbook) {
		// 设置列头的格式
		Font detFont = workbook.createFont();
		detFont.setFontHeightInPoints((short)BaseConfig.FONT_DEFAULT_POINT_SIZE);
		detFont.setColor((short)FONT_DETAULT_COLOR);
		detFont.setUnderline(Font.U_NONE);
		detFont.setBoldweight(Font.BOLDWEIGHT_BOLD);

		CellStyle cellStyle = workbook.createCellStyle();
		
		cellStyle.setAlignment(CellStyle.ALIGN_CENTER);
		cellStyle.setFont(detFont);
		cellStyle.setWrapText(true);
		cellStyle.setBorderBottom(CellStyle.BORDER_THIN);
		cellStyle.setBorderLeft(CellStyle.BORDER_THIN);
		cellStyle.setBorderRight(CellStyle.BORDER_THIN);
		cellStyle.setBorderTop(CellStyle.BORDER_THIN);
		
		Row row = sheet.createRow(currentRow);
		// 输出列头
		for (int col = 0; col < cellColArr.length; col++) {
			// 输出
			Cell cell = row.createCell(col);
			cell.setCellStyle(cellStyle);
			cell.setCellValue(cellColArr[col].getColHeader());
		}
		currentRow++;
		return currentRow;
	}

	/**
	 * 设置excel标题。
	 * @param titleConfig 标题输出配置
	 * @param totalColNum 当前表格的总列数。
	 * @param currentOutputRow 当前输出开始行。重0开始 。
	 * @param sheet 输出excel的sheet。
	 * @return 返回标题占的行数。
	 */
	private int setTitle(TitleConfig titleConfig, int totalColNum,
			int currentOutputRow, Sheet sheet, Workbook workbook) {
		int currentRow = currentOutputRow;
		if (titleConfig != null) {
			String title = titleConfig.getContent();
			// 标题格式
			CellStyle cellStyle = createTitelStyle(titleConfig, workbook);
			
			// 输出
			Row row = sheet.createRow(currentRow);
			Cell cell = row.createCell(0);
			cell.setCellStyle(cellStyle);
			cell.setCellValue(title);
			
			sheet.addMergedRegion(new CellRangeAddress(currentRow, currentRow, 
					0, totalColNum - 1));
			row.setHeight((short) getExcelHeight(titleConfig.getHeight()));
			currentRow++;
		}

		return currentRow - currentOutputRow;
	}

	/**
	 * 设置单元格式的排列方式。
	 * @param align 对其方式
	 * @param cellStyle 单元格样式
	 * 
	 */
	private void setAlignment(Align align, CellStyle cellStyle) {
		if (align == Align.LEFT) {
			cellStyle.setAlignment(CellStyle.ALIGN_LEFT);
		} else if (align == Align.CENTER) {
			cellStyle.setAlignment(CellStyle.ALIGN_CENTER);
		} else if (align == Align.RIGHT) {
			cellStyle.setAlignment(CellStyle.ALIGN_RIGHT);
		}
	}

	/**
	 * 转换为excel的宽度。
	 * @param width
	 * @return
	 */
	private int getExcelWidth(float width) {
		return (int) (width * 80);
	}

	/**
	 * 转换为excel的高度。
	 * @param height
	 * @return
	 */
	private int getExcelHeight(float height) {
		return (int) height * 80;
	}

	/**
	 * 获取临时文件的目录路径
	 * @return 临时文件的目录路径
	 */
	protected String getTempDic() {
		String tempDic = "";
		if(exportConfiguration != null) tempDic = exportConfiguration.getTempExcelStoreLocation();
		if(tempDic == null || tempDic.trim().equals("")) 
			tempDic = ExportConfiguration.DEFAULT_TEMP_FILE_DIC;	
		return tempDic.replace("\\", "/");	
	}
	
	/**
	 * 获取临时的excel文件名称，
	 * 	如果exportConfiguration里面有配置，则使用此名称
	 * 如果没有配置则使用System.nanoTime()
	 * @return 获取临时的excel文件名称
	 * */
	protected String getTempExcelFileName() {
		String tempFileName = "";
		if(exportConfiguration != null) tempFileName = exportConfiguration.getExcelFileName();
		if(tempFileName == null || tempFileName.trim().equals(""))
			tempFileName = String.valueOf(System.nanoTime());
		return tempFileName;
	}
	
	/**
	 * 根据excel版本配置获取对应的workbook对象
	 * @param in excel文件的输入流
	 * @throws IllegalArgumentException 不支持excel的版本 
	 * */
	protected Workbook getWorkbookByConfigExcelVersion(InputStream in) throws IOException, 
		InvalidFormatException {
		if(in != null) {
			if (!in.markSupported()) {  
		        in = new PushbackInputStream(in, 8);  
		    }  
		    if (POIFSFileSystem.hasPOIFSHeader(in)) {  
		        return new HSSFWorkbook(in);  
		    }  
		    if (POIXMLDocument.hasOOXMLHeader(in)) {  
		        return new XSSFWorkbook(OPCPackage.open(in));  
		    }  
		    throw new IllegalArgumentException("你的excel版本目前poi解析不了");  
		}
		if(exportConfiguration != null && exportConfiguration.getExcelVersion() != null&&
				ExcelVersion.EXCEL_2007.equals(exportConfiguration.getExcelVersion())) {
			return new XSSFWorkbook();
		}
		return new HSSFWorkbook();
	}
	
	/**
	 * 根据配置的excel版本获取excel文件的后缀名
	 * */
	protected String getExcelSuffixNameByConfigExcelVersion() {
		if(exportConfiguration != null && exportConfiguration.getExcelVersion() != null &&
				ExcelVersion.EXCEL_2007.equals(exportConfiguration.getExcelVersion())) {
			return EXCEL_2007_SUFFIX;
		}
		return EXCEL_2003_SUFFIX;
	}
	
	/**
	 * 导出Excel文件流
	 * <p><b>注意</b>
	 * 		如果是有设置exportConfiguration对象，
	 * 		并且设置isSeparateExcel为true，
	 * 		那么最后导出的是个excel的zip压缩包，并且传递到方法中的
	 * 	   <pre>list</pre>对象执行完成后会被清空数据，并被置为null
	 * </p>
	 * @param list 包含需导出数据的对象list。
	 * @param tableConfig 导出表格配置。
	 * @param out 输出流。
	 * @return 成功返回导出条数，失败返回-1。
	 */
	@Override
	public int exportExcel(List<T> list, TableConfig<T> tableConfig,
			OutputStream out) {
		return exportExcel(list, tableConfig, null, out);
	}

	/**
	 * 导出Excel文件流
	 * <p><b>注意</b>
	 * 		如果是有设置exportConfiguration对象，
	 * 		并且设置isSeparateExcel为true，
	 * 		那么最后导出的是个excel的zip压缩包，并且传递到方法中的
	 * 	   <pre>list</pre>对象执行完成后会被清空数据，并被置为null
	 * </p>
	 * @param list 包含需导出数据的对象list。
	 * @param tableConfig 导出表格配置。
	 * @param in 输入流。 当in不为null时，根据输入的excel文件基础上创建输出表格。
	 * @param out 输出流。
	 * @return 成功返回导出条数，失败返回-1。
	 */
	@Override
	public int exportExcel(List<T> list, TableConfig<T> tableConfig,
			InputStream in, OutputStream out) {
		validateParameters(list, tableConfig, out);

		List<CellColConfig<T>> cellColConfigList = tableConfig.getCellCols();
		if (cellColConfigList == null || cellColConfigList.size() == 0)
			throw new IllegalArgumentException("The column config is empty");
		int result = 0;
		// 获得显示列的配置。
		CellColConfig<T>[] cellColArr = CellColConfig.getShowCols(cellColConfigList);
		// 添加序号头
		if (tableConfig.isShowSerialNumber()) cellColArr = exportRowDataHolder.addSerialNumber(cellColArr);
		int totalColNum = cellColArr.length;
		
		try {
			if(exportConfiguration != null && exportConfiguration.isSeparateExcel()) {
				result += sectionBuildExcel(list, tableConfig);
//				this.closeAndExport(out);
				exportToZipFile(out);
				close();
			} else {
				result = exportOneWorkbook(list, tableConfig, in, out, totalColNum, cellColArr, result);
			}
		} catch (FileNotFoundException e) {
			result = -1;
			log.error(e.getMessage(), e);
		} catch (IOException e) {
			result = -1;
			log.error(e.getMessage(), e);
		} catch (ExportException e) {
			result = -1;
			log.error(e.getMessage(), e);
		} finally {
			close();
		}
		// 销毁当前线程实例
		ExportFactory.getInstance().destroyExportExcel();
		return result;
	}

	/**
	 * 分段生成excel
	 * @param list 要生成的数据
	 * @param tableConfig excel配置
	 * @throws ExportException 
	 */
	protected int sectionBuildExcel(List<T> list, TableConfig<T> tableConfig) throws ExportException {
		int result = 0;
		final int separator = exportConfiguration.getDataSizePerExcel();
		final int counter = ((Double)Math.ceil((double)list.size() / (double)separator)).intValue();
		for(int i = 0; i < counter; i++) {
			final int toIndex = (i + 1) * separator; 
			List<T> subList = list.subList(i * separator, toIndex > list.size() ? list.size() : toIndex);
			result += makeTempExcel(subList, tableConfig);
		}
		list = null;
		return result;
	}
	
	/**
	 * 可以利用当前实例进行多次导出，最后必须调用<pre>close()</pre>方法，显示进行关闭
	 * @param list 需要导出的数据
	 * @param tableConfig 导出配置
	 * @return 返回正确导出数据的条数
	 * @throws ExportException 导出过程中船舷异常
	 * @throws IllegalArgumentException 导出数据为空时
	 * @throws IllegalArgumentException tableConfig为空时
	 * @throws IllegalArgumentException column config为空时
	 */
	@Override
	public int multiExport(List<T> list, TableConfig<T> tableConfig) throws ExportException {
		if(list == null) throw new IllegalArgumentException("List data must not null");
		if(tableConfig == null) throw new IllegalArgumentException("Table config must not null");
		
		if(exportConfiguration != null && exportConfiguration.isSeparateExcel()) {
			result += sectionBuildExcel(list, tableConfig);
		} else {
			result += makeTempExcel(list, tableConfig);
		}
		return result;
	}
	
	
//	/**
//	 * 关闭并且导出
//	 * 导出的是个zip压缩格式的包
//	 * @param out 输出流
//	 * @throws ExportException 关闭导出的时候出现异常
//	 */
//	@Override
//	public void closeAndExport(OutputStream out) throws ExportException {
//		zip(out);
//		tempExcelDicName = "";
//		currentFileCounter = 0;
//		result = 0;
//	}
	
	/**
	 * 关闭export对象
	 */
	@Override
	public void close() {
		ExportFactory.getInstance().destroyExportExcel();
		if(tempExcelDicName == null || tempExcelDicName.trim().equals("")) return;
		File tempDic = new File(tempExcelDicName);
		if(tempDic.exists()) {
			File[] files = tempDic.listFiles();
			for(File f  : files) {
				if(f != null) f.delete();
			}
			tempDic.delete();
		}
		tempExcelDicName = "";
		currentFileCounter = 0;
		result = 0;
	}

	/**
	 * 打包成zip文件并且导出
	 */
	@Override
	public void exportToZipFile(OutputStream out) throws ExportException {
		zip(out);
	}
	
}
